#include <dim-sum/errno.h>
#include <dim-sum/bug.h>
#include <dim-sum/boot_allotter.h>
#include <dim-sum/cache.h>
#include <dim-sum/init.h>
#include <dim-sum/process.h>
#include <dim-sum/mm_types.h>
#include <dim-sum/beehive.h>
#include <dim-sum/mmu.h>

#include <dim-sum/memory_regions.h>
#include <asm/early_map.h>
#include <asm/mmu_context.h>
#include <asm/memory.h>


void mmu_set_tlb(struct mmu_desc* mmu)
{
	return arch_mmu_set_tlb(mmu);
}


static mmu_l1_t *mmu_get_l1(struct mmu_desc *mmu, virt_addr_t addr)
{
	return arch_mmu_get_l1(mmu, addr);
}

static int mmu_ttbl_fault_core(struct mmu_walk_info *info)
{
	return arch_mmu_ttbl_fault(info);
}

/**
 * MMU的转换表异常处理接口函数
 */
int mmu_ttbl_fault(struct mmu_walk_info *info)
{
	if (!info || !info->in_mmu || !info->in_faddr)
		return -EFAULT;
	return mmu_ttbl_fault_core(info);
}

/**
 * MMU权限处理，先要调用MMU的转换表异常处理接口函数
 * 处理好转换表，中间的转换表不可能有权限问题，只可能出现在最后一级
 * 同样基于同一个mmu_walk_info
 */
int mmu_ttbl_permission(struct mmu_walk_info *info)
{
	return arch_mmu_ttbl_permission(info);
}

/**
 * MMU完成映射，先要调用MMU的转换表异常处理接口函数
 * 并且基于同一个mmu_walk_info，其中要分配一个数据页面
 */
int mmu_complete_map(struct mmu_walk_info *info)
{
	return arch_mmu_complete_map(info);
}

int mmu_kernel_rw_map(struct mmu_walk_info *info)
{
	return arch_mmu_kernel_rw_map(info);
}

static int mmu_find_ttbl_core(struct mmu_walk_info *info)
{
	return arch_mmu_find_ttbl(info);
}

int mmu_find_ttbl(struct mmu_walk_info *info)
{
	mmu_l1_t *mmu_l1;
	mmu_l1 = mmu_get_l1(info->in_mmu, info->in_faddr);
	if (!mmu_l1) {
		info->out_l1 = NULL;
		return MMU_MAP_L1_TBL;
	}
	info->out_l1 = mmu_l1;
	if (!info || !info->in_faddr || !info->in_mmu)
		return -EFAULT;
	return mmu_find_ttbl_core(info);
}


/**
 * MMU解除映射一个虚拟地址
 */
static int mmu_unmap_vaddr(struct mmu_walk_info *info)
{
	return arch_mmu_unmap_vaddr(info);
}

/**
 * MMU断开映射一个虚拟地址
 */
int mmu_cutoff_vaddr(struct mmu_walk_info *info)
{
	return arch_mmu_cutoff_vaddr(info);
}

/**
 * MMU接通映射一个虚拟地址
 */
int mmu_connect_vaddr(struct mmu_walk_info *info)
{
	if (mmu_ttbl_fault(info) != MMU_MAP_L3_TBL)
		return -EFAULT;

	return mmu_complete_map(info);
}

/**
 * MMU解除映射一个虚拟地址空间实现
 */
static int mmu_unmap_range_core(struct mmu_walk_info *info)
{
	virt_addr_t start;
	int ret;

	for (start = info->in_umstart; start < info->in_umend; start += PAGE_SIZE) {
		info->in_faddr = start;
		ret = mmu_unmap_vaddr(info);
		if (ret)
			return ret;
	}

	return 0;
}

/**
 * MMU解除映射一个虚拟地址空间
 */
int mmu_unmap_range(struct mmu_walk_info *info)
{
	if (!info)
		return -EINVAL;
	if (!info->in_umstart || !info->in_umend)
		return -EINVAL;
	if (info->in_umstart > info->in_umend)
		return -EINVAL;
	if (info->in_umstart == info->in_umend)
		return 0;

	return mmu_unmap_range_core(info);
}

/**
 * MMU映射一个虚拟地址
 */
static int mmu_map_vaddr(struct mmu_walk_info *info)
{
	return arch_mmu_map_vaddr(info);
}

/**
 * MMU映射一个虚拟地址空间实现
 */
static int mmu_map_range_core(struct mmu_walk_info *info)
{
	virt_addr_t start;
	int ret;

	for (start = info->in_umstart; start < info->in_umend; start += PAGE_SIZE) {
		info->in_faddr = start;
		ret = mmu_map_vaddr(info);
		if (ret)
			return ret;
	}

	return 0;
}

/**
 * MMU映射一个虚拟地址空间
 */
int mmu_map_range(struct mmu_walk_info *info)
{
	if (!info)
		return -EINVAL;
	if (!info->in_mapstart || !info->in_mapend)
		return -EINVAL;
	if (info->in_mapstart > info->in_mapend)
		return -EINVAL;
	if (info->in_mapstart == info->in_mapend)
		return 0;

	return mmu_map_range_core(info);
}

/**
 * MMU修改映射一个虚拟地址属性
 */
static int mmu_mprotect_vaddr(struct mmu_walk_info *info)
{
	return arch_mmu_mprotect_vaddr(info);
}

/**
 * MMU修改一个虚拟地址空间属性实现
 */
static int mmu_mprotect_range_core(struct mmu_walk_info *info)
{
	virt_addr_t start;
	int ret;

	for (start = info->in_mprotstart;
			start < info->in_mprotend;
			start += PAGE_SIZE) {
		info->in_faddr = start;
		ret = mmu_mprotect_vaddr(info);
		if (ret)
			return ret;
	}

	return 0;
}

/**
 * MMU修改一个虚拟地址空间属性
 */
int mmu_mprotect_range(struct mmu_walk_info *info)
{
	if (!info)
		return -EINVAL;
	if (!info->in_mprotstart || !info->in_mprotend)
		return -EINVAL;
	if (info->in_mprotstart == info->in_mprotend)
		return 0;

	return mmu_mprotect_range_core(info);
}


/**
 * 修改映射MMU的四级页所对应page
 */
static int mmu_dirty_vaddr(struct mmu_walk_info *info)
{
	return arch_mmu_dirty_vaddr(info);
}

/**
 * MMU修改一个虚拟地址空间所对应页属性实现
 */
static int mmu_dirty_range_core(struct mmu_walk_info *info)
{
	virt_addr_t start;
	int ret;

	for (start = info->in_dirtystart;
			start < info->in_dirtyend;
			start += PAGE_SIZE) {
		info->in_faddr = start;
		ret = mmu_dirty_vaddr(info);
		if (ret)
			return ret;
	}

	return 0;
}

/**
 * MMU修改一个虚拟地址空间所对应页属性
 */
int mmu_dirty_range(struct mmu_walk_info *info)
{
	if (!info)
		return -EINVAL;
	if (!info->in_dirtystart || !info->in_dirtyend)
		return -EINVAL;
	if (info->in_dirtystart == info->in_dirtyend)
		return 0;

	return mmu_dirty_range_core(info);
}


static int mmu_clean_tbl_core(struct mmu_walk_info *info)
{
	return arch_mmu_clean_tbl(info);
}

int mmu_clean_tbl(struct mmu_walk_info *info)
{
	if (!info)
		return -EINVAL;
	if (!info->in_mmu)
		return -EINVAL;
	return mmu_clean_tbl_core(info);
}

int mmu_create(struct process_desc* proc)
{
	if (!proc || !proc->mem)
		return -EINVAL;

	return arch_create_mmu(proc->mem);
}

int mmu_exit(struct process_desc* proc, struct mem_desc* m)
{
	if (!proc || !m)
		return -EINVAL;
	
	return arch_exit_mmu(proc, m);
}

int mmu_set_info_prot(struct mmu_walk_info *info, u64 access)
{
	unsigned long mmu_prot = 0;

	if (!info || !access)
		return -EINVAL;
	
	/**
	 * 根据vma的访问特性，配置页表的访问执行属性
	 */
	if (access & VAC_USER) {
		if (access & VAC_READ)
			mmu_prot |= (PROT_READ | PROT_USER);
		if (access & VAC_WRITE) {
			mmu_prot |= (PROT_WRITE | PROT_USER);
		}
		if (access & VAC_EXEC) {
			mmu_prot |= PROT_EXEC;
			if (!(access & VAC_WRITE) && !(access & VAC_READ))
				mmu_prot |= (PROT_READ | PROT_USER);
		}
	} else {
		/* 内核态永远可读写 */
	}
	return arch_mmu_set_info_prot(info, mmu_prot);
}


pt_l2_t * follow_pt_l2(struct memory_map_desc *desc, unsigned long addr)
{
	pt_l1_t *pt_l1 = pt_l1_ptr(desc->pt_l1, addr);

	BUG_ON(pt_l1_is_empty(*pt_l1) || pt_l1_is_invalid(*pt_l1));

	return pt_l2_ptr(pt_l1, addr);
}

pt_l3_t * follow_pt_l3(struct memory_map_desc *desc, unsigned long addr)
{
	pt_l2_t *pt_l2 = follow_pt_l2(desc, addr);
	BUG_ON(pt_l2_is_empty(*pt_l2) || pt_l2_is_invalid(*pt_l2));

	return pt_l3_ptr(pt_l2, addr);
}

pt_l4_val_t * follow_pt_l4(struct memory_map_desc *desc, unsigned long addr)
{
	pt_l3_t *pt_l3 = follow_pt_l3(desc, addr);

	BUG_ON(pt_l3_is_empty(*pt_l3) || pt_l3_is_invalid(*pt_l3));

	return pt_l4_ptr(pt_l3, addr);
}
